package com.wei.czz.framework.game.service.impl;

import com.wei.czz.common.constant.RedisConstant;
import com.wei.czz.common.dto.game.GamePrizeDto;
import com.wei.czz.common.enums.CommonEnum;
import com.wei.czz.common.enums.ResultEnum;
import com.wei.czz.common.enums.rabbitMQ.MqEnum;
import com.wei.czz.common.exception.CzzException;
import com.wei.czz.common.po.game.GamePo;
import com.wei.czz.common.po.game.GamePrizePo;
import com.wei.czz.common.po.game.GameUserPo;
import com.wei.czz.common.utils.SecurityUtils;
import com.wei.czz.framework.common.handler.redis.RedisHashHandler;
import com.wei.czz.framework.common.handler.redis.RedisSetHandler;
import com.wei.czz.framework.common.handler.redis.RedisHandler;
import com.wei.czz.framework.common.helper.RabbitHelper;
import com.wei.czz.framework.common.service.AsyncService;
import com.wei.czz.framework.game.entity.GamePrizeEntity;
import com.wei.czz.framework.game.service.*;
import lombok.AllArgsConstructor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * Created by IntelliJ IDEA.
 *
 * @author wei
 * date: 2023-03-23 16:40:45
 * className: LuckDrawServiceImpl 抽奖请求服务接口实现类
 * version: 1.0
 * description:
 */
@Service("luckDrawService")
@AllArgsConstructor
public class LuckDrawServiceImpl implements LuckDrawService {

    private static final Logger log = LoggerFactory.getLogger(LuckDrawServiceImpl.class);

    private final GameService gameService;

    private final GameFlowService gameFlowService;

    private final GamePrizeService gamePrizeService;

    private final GamePrizeFlowService gamePrizeFlowService;

    private final GameUserFlowService gameUserFlowService;

    private final RedisHandler redisHandler;

    private final RedisSetHandler redisSetHandler;

    private final RedisHashHandler redisHashHandler;

    private final RabbitHelper rabbitHelper;

    private final AsyncService asyncService;

    @Override
    public GamePrizeDto luckDraw(Long gameId) {
        // 获取正在抽奖的用户主键
        Long optUserId = SecurityUtils.getUserId();
        Date date = new Date();

        GamePo gamePo = gameService.getGamePo(gameId);
        if (gamePo.getStartTime().compareTo(date) > 0) {
            throw new CzzException(ResultEnum.REQUEST_FAIL.getCode(), "游戏还没开始，敬请期待。");
        }
        if (gamePo.getEndTime().compareTo(date) < 0) {
            throw new CzzException(ResultEnum.REQUEST_FAIL.getCode(), "游戏已经结束，请确认。");
        }
        if (gamePo.getStatus().equals(CommonEnum.ONE.getValue())) {
            throw new CzzException(ResultEnum.REQUEST_FAIL.getCode(), "游戏已暂停，请确认。");
        }

        // 扣减用户游戏次数
        Long gameNum = redisHandler.greaterAndDecrement(RedisConstant.GAME_USER_MAP + gameId, optUserId.toString(), 0);
        if (gameNum.equals(-2L)) {
            throw new CzzException(ResultEnum.REQUEST_FAIL.getCode(), "您当前不可以参与游戏，请确认。");
        }
        if (gameNum.equals(-1L)) {
            throw new CzzException(ResultEnum.REQUEST_FAIL.getCode(), "您游戏次数已用完，请确认。");
        }

        String poolKey = RedisConstant.GAME_PRIZE_SET + gameId;
        // 进行抽奖
        GamePrizePo gamePrizeBo = redisSetHandler.randomMember(poolKey);
        if (gamePrizeBo == null) {
            // 恢复用户游戏次数
            this.asyncAddUserGameNum(optUserId, gameId, gameNum);

            log.error("游戏奖品池缓存失效，系统出现严重问题，请务必立即处理。");
            throw new CzzException("奖品池为空，抽奖失败，请务必及时处理");
        }

        Long finalPrizeId = gamePrizeBo.getPrizeId();

        // 扣减奖品剩余数量
        Long prizeNum = redisHandler.greaterAndDecrement(RedisConstant.GAME_PRIZE_MAP, gamePrizeBo.getPrizeId().toString(), 0);
        if (prizeNum.equals(-2L)) {
            // 恢复用户游戏次数
            this.asyncAddUserGameNum(optUserId, gameId, gameNum);

            log.info("游戏奖品缓存不存在，请务必及时处理");
            throw new CzzException("游戏奖品缓存不存在，请务必及时处理");
        }
        if (prizeNum.equals(-1L)) {
            finalPrizeId = gamePo.getDefaultPrizeId();
            log.warn("奖品被抽完，给予默认奖品。prizeId={} finalPrizeId={}", gamePrizeBo.getPrizeId(), finalPrizeId);
        }
        if (prizeNum.equals(0L)) {
            // 奖品被抽完，清理奖品池的奖品数据
            asyncService.execute(() -> {
                log.info("开始清理奖品池的奖品数据。prizeId={}", gamePrizeBo.getPrizeId());

                // 获取游戏奖品
                GamePrizeEntity gamePrize = gamePrizeService.getById(gamePrizeBo.getPrizeId());

                List<GamePrizePo> gamePrizePoList = new ArrayList<>(gamePrize.getProbability());
                int length = gamePrize.getProbability();
                for (int i = 0; i < length; i++) {
                    gamePrizePoList.add(new GamePrizePo(gamePrize.getId(), i));
                }

                // 删除缓存
                Long count = redisSetHandler.remove(poolKey, gamePrizePoList);
                log.info("从奖品池中去除奖品操作完成。count={}", count);
            });
        }
        // 记录游戏流水
        gameFlowService.saveGameFlow(gameId, optUserId, gamePrizeBo.getPrizeId(), finalPrizeId);

        asyncService.execute(() -> {
            log.info("开始记录奖品和用户操作流水。prizeId={} userId={}", gamePrizeBo.getPrizeId(), optUserId);

            String content;
            if (prizeNum.compareTo(0L) >= 0) {
                content = "用户抽奖，奖品被抽中。剩余数量【" + prizeNum + '】';
                gamePrizeFlowService.saveGamePrizeFlow(gamePrizeBo.getPrizeId(), "奖品扣减", content, CommonEnum.ZERO.getLongValue());
            }
            content = "用户抽奖，剩余次数【" + gameNum + '】';
            gameUserFlowService.saveGameUserFlow(optUserId, gameId, "次数扣减", content, CommonEnum.ZERO.getLongValue());

            log.info("记录奖品和用户操作流水完成");
        });

        if (prizeNum.compareTo(0L) >= 0) {
            rabbitHelper.send(MqEnum.GAME_PRIZE, gamePrizeBo.getPrizeId());
        }
        GameUserPo gameUserPo = new GameUserPo();
        gameUserPo.setUserId(optUserId)
                        .setGameId(gameId);
        rabbitHelper.send(MqEnum.GAME_USER, gameUserPo);

        GamePrizeDto gamePrizeDto = new GamePrizeDto();
        gamePrizeDto.setId(finalPrizeId.toString());
        return gamePrizeDto;
    }

    /**
     * 异步增加用户游戏次数
     * @param userId  用户主键
     * @param gameNum 用户剩余游戏次数
     */
    private void asyncAddUserGameNum(Long userId, Long gameId, Long gameNum) {
        asyncService.execute(() -> {
            log.info("开始恢复用户的游戏次数。userId={} gameNum={}", userId, gameNum);
            Long count = redisHashHandler.increment(RedisConstant.GAME_USER_MAP, userId.toString(), 1);
            log.info("恢复用户的游戏次数完成。count={}", count);

            String remark = "用户抽奖遇到异常，恢复游戏次数 原【" + gameNum + "】新【" + count + '】';
            // 记录用户流水
            gameUserFlowService.saveGameUserFlow(userId, gameId, "恢复游戏次数",remark, CommonEnum.ZERO.getLongValue());

        });
    }

}
